import java.io.*;
import java.util.regex.*;
import java.util.*;

public class Translator{
	// translation dictionary
	public HashMap<String, String> dictionary = new HashMap<String, String>();
	// translation dictionary, with POS
	public HashMap<String, ArrayList<Word>> dictionaryPOS = new HashMap<String, ArrayList<Word>>();
	// french dictionary, with POS
	public HashMap<String, Word> frenchWordsPOS = new HashMap<String, Word>();
	// english dictionary, with POS computed from translation
	public ArrayList<Word> englishWordsPOS = new ArrayList<Word>();
	// words from the french text
	public ArrayList<String> frenchText = new ArrayList<String>();
	// words into english text
	public ArrayList<Word> englishText = new ArrayList<Word>();	


	//reads a dictionary file into frenchWordsPOS and englishWordsPOS, 
    //which are arrayLists of Word objects containing a word and its part of speech
    //and also dictionaryPOS, which is a Hashmap containing french words as keys and
    //and ArrayList of Words which contain all translations of that french word as key-values
	public void readDictionaryFile(File dictFile){
		try{
			BufferedReader input = new BufferedReader(new FileReader(dictFile));
			for(String line = input.readLine(); line != null; line = input.readLine()) {
				if(line.charAt(0)!='/'){ //if the line isn't a comment
					// separate the french from the english part
					String[] words = line.split(">");
					

					// french word : get the POS and word
					String[] wordsFrench = words[0].split(":");
					String wordFrench = wordsFrench[1].trim();
					String[] POS = wordsFrench[0].split(",");
					Word wf = new Word(wordFrench);
					for (int i=0; i<POS.length; i++){ //add all possible part of speeches of the word
						wf.POS.add(POS[i].trim());
					}
					// store in french word list
					frenchWordsPOS.put(wordFrench, wf);

					// english word : get the several translations and POSs
					ArrayList<Word> translations = new ArrayList<Word>();
					String[] wordsEnglish = words[1].trim().split(",");

					// extract the POS of the english words
					for(int i=0; i<wordsEnglish.length; i++){
						Word we;
						if (wordsEnglish[i].length()>=1){

							String[] parts = wordsEnglish[i].split(":");
							we = new Word( parts[1].trim(), parts[0].trim());

							if (parts.length==3){
								we.setInfinitive(parts[2]);
							}

						}
                        else{
							we = new Word(null, null);
						}
						translations.add(we);
					}

                    // add to dictionaryPOS, frechWords and englishWords dictionaries
					dictionaryPOS.put(wordFrench, translations);
					for(int i=0; i<translations.size(); i++){
						englishWordsPOS.add(translations.get(i));
					}
					
					// add to dictionary hashmap : old version
					String wordEnglish; 
					if (wordsEnglish[0].length()>=1){
						wordEnglish = wordsEnglish[0].split(":")[1].trim();
					}else{
						wordEnglish = "";
					}
					dictionary.put(wordFrench, wordEnglish);
				}
			}
		}catch(IOException e){
			System.out.println(e.getMessage());
		}
	}

    //reads a text file of french text    
	public void readFrenchText(File frenchFile){
		try{
			BufferedReader input = new BufferedReader(new FileReader(frenchFile));
			for (String line= input.readLine(); line!=null; line = input.readLine()){
				String[] words = line.split(" "); //extract each word

				for (int i=0; i<words.length; i++){ //go through each word
					if (words[i].matches(".*\\p{Punct}.*")){ //if a word has any type of punctuation

			            List<String> toAdd = new ArrayList<String>();
			            String word = words[i];

						if(word.contains(",")){
							word = word.split(",")[0].trim();
				            toAdd.add(0, ",");
						} 
						if(word.contains(".")){
							word = word.split("\\.")[0].trim();
				            toAdd.add(0, ".");
						}
						if(word.contains(":")){
							word = word.split(":")[0].trim();
				            toAdd.add(0, ":");
						}
						if(word.contains("\"")){ //if word has a quotation mark "
							word = word.trim();

							if(word.indexOf("\"")==0){ //if the quotation mark is at the beginning
								frenchText.add("\"");
								word = word.split("\"")[1].trim();
							}
                            else{
								word = word.split("\"")[0].trim();
								toAdd.add(0, "\"");
							}
						}

						if(word.contains("'")){ //split around the apostrophe, and make it into two seperate words
							String word2 = word.split("'")[1].trim();
							word = word.replace(word2, ""); //take out word2 (the part after the apostrophe) from word
							toAdd.add(0,word2);
						}

						// add the word, and all following punctuations and things in toAdd
						frenchText.add(word);
						for (int k=0; k<toAdd.size(); k++){
							if (!toAdd.get(k).equals("\n")){
								frenchText.add(toAdd.get(k));
							}
						}
					}
                    else{ //if this word has no punctuation, add it
						frenchText.add(words[i].trim());
					}
				}

                //add an end of line marker for each line
                frenchText.add("\n");

			}
		}catch (IOException e){
			System.out.println(e.getMessage());
		}
	}
	
	public void translateToEnglish(){
		for (int i=0; i<frenchText.size(); i++){
			String frenchWord = frenchText.get(i);
			
			// if we find a negation : translate it to an english negation
			// French negations are : "ne [verb] pas" or "n' [verb] pas" -> "didn't [verb]", or also "ne [verb] rien"-> "didn't [verb] anything"
			// We use the infinitive of the english verb, and the tense of the french verb, to get the right translation
			// e.g. "didn't understand anything" instead of "don't understood anything"
			String negationType = "none";
			int verbPosition = 1;
			if((i<frenchText.size()-2) && (frenchWord.equals("ne") || frenchWord.equals("n'"))){

				Word maybeVerb = frenchWordsPOS.get(frenchText.get(i+1));
				if(maybeVerb.getPOS().get(0).equals("pron")){
					maybeVerb = frenchWordsPOS.get(frenchText.get(i+2));
					verbPosition = 2;
				}
				if (maybeVerb.getPOS().get(0).charAt(0)=='v'){

				    String tense = maybeVerb.getPOS().get(0).split("/")[1].trim();
					// case "ne [verb] pas" -> "didn't [verb]"
					if (frenchText.get(i+verbPosition+1).equals("pas")){

						negationType = "pas:"+tense;
						if (verbPosition==2){
							negationType = negationType + ":" + frenchText.get(i+1);
						}
					// case "ne [verb] rien" -> "didn't [verb] anything"
					}else if(frenchText.get(i+verbPosition+1).equals("rien")){

						negationType = "rien:"+tense;
						if (verbPosition==2){
							negationType = negationType + ":" + frenchText.get(i+1);
						}
					}
				}
				
			}
			if(!negationType.equals("none")){

				String[] split = negationType.split(":");
				String tense = split[1];
				String pronoun = split[0];				
				if(tense.equals("PAST")){
					englishText.add(new Word("didn't", "v"));
				}else if(tense.equals("PRES")){
					if(frenchText.get(i-1).equals("je")){
						englishText.add(new Word("don't", "v"));
					}else{
						englishText.add(new Word("doesn't", "v"));
					}
				}else if(tense.equals("FUT")){
					englishText.add(new Word("won't", "v"));
				}else if(tense.equals("COND")){
					englishText.add(new Word("wouldn't", "v"));
				}else{
					englishText.add(new Word("didn't", "v"));
				}
				// get the infinitive of the verb if it has one
				Word englishVerb = dictionaryPOS.get(frenchWordsPOS.get(frenchText.get(i+verbPosition)).getWord()).get(0);
				if (englishVerb.hasInfinitive()){
					englishText.add(new Word(englishVerb.getInfinitive(), "v"));
				}else{
					englishText.add(englishVerb);
				}	
				// get the pronoun if there is one
				if (verbPosition == 2){
					englishText.add(dictionaryPOS.get(split[2]).get(0)); 
				}
				// add adverb if needed
				if(pronoun.equals("rien")){
					englishText.add(new Word("anything", "pron"));
				}

				// skip to the next word
				i = i+1+verbPosition;

			}else{ // else no negation, translate word-per-word
				if(dictionaryPOS.containsKey(frenchWord.toLowerCase())){ //if the word is the french-english dictionary
					Word w = dictionaryPOS.get(frenchWord.toLowerCase()).get(0);
					if(!w.getWord().equals("null"))
						englishText.add(w);
				}
				else{ //if the word isn't in the french english dictionary, (like , or "), just put it in
					Word toAdd = new Word(frenchWord);
					englishText.add(toAdd);
				}
			}

		}


        //rearrange words according to handwritten patterns
        for (int i = 0; i<(englishText.size() - 1); i++){

          //fix all adjectives that come after nouns
          //such as 'lesson particular' to "particular lesson"
          if( (englishText.get(i).getPOS().contains("n") || englishText.get(i).getPOS().contains("pron") )
                    && englishText.get(i+1).getPOS().contains("adj") ){
                Word noun = englishText.get(i);
                Word adj = englishText.get(i+1);
                englishText.set(i, adj);
                englishText.set(i+1, noun);
          }

          //the special pattern:  ", v prn/n = ", prn/n v
          //such as ", said he = ", he said
          if(i < (englishText.size() - 3) ){ //if i is not near the end
              if(englishText.get(i).getWord().equals("\"") && englishText.get(i+1).getWord().equals(",") &&
                     englishText.get(i+2).getPOS().contains("v") && 
                     ( englishText.get(i+3).getPOS().contains("pron") || englishText.get(i+3).getPOS().contains("n") ) ){

                  Word verb = englishText.get(i+2);
                  Word noun = englishText.get(i+3);
                  englishText.set(i+2, noun);
                  englishText.set(i+3, verb);
                }
          }

            
          //check for 3 word patterns first
          if(englishText.get(i).hasPOS() && i < (englishText.size() - 2)){
              
            //rearrange according to a handwritten rule - det n/pron1 n/pron2 = det n/pron1
            //such as the voice me had seemed ... = the voice had seemed ...
            //this grouping of pronouns happens alot, its usually sensible just to delete the second
            if(englishText.get(i).getPOS().contains("det") && 
                    (englishText.get(i+1).getPOS().contains("n") || englishText.get(i+1).getPOS().contains("pron") )
                    && (englishText.get(i+2).getPOS().contains("pron") || englishText.get(i+2).getPOS().contains("n") ) ){
                
                englishText.set(i+2, new Word(""));
             }

            //pron/n1 pron/n2 v = pron/n1 v
            //such as he Alex went = he went
            //this grouping of pronouns happens alot, its usually sensible just to delete the second
            if( (englishText.get(i).getPOS().contains("pron") || englishText.get(i).getPOS().contains("n") ) &&
                (englishText.get(i+1).getPOS().contains("pron") || englishText.get(i+1).getPOS().contains("n") ) &&
                englishText.get(i+2).getPOS().contains("v") ){

                englishText.set(i+1, new Word(""));
            }

            //rearrange according to a handwritten rule - pron\n det v = pron\n v
            //such as alex the kicked = alex kicked
            //this also happens a lot, it usually makes things better to just delete the determinant
            if((englishText.get(i).getPOS().contains("pron") || englishText.get(i).getPOS().contains("n") ) &&
                 englishText.get(i+1).getPOS().contains("det") && englishText.get(i+2).getPOS().contains("v") ){
                
                englishText.set(i+1, new Word(""));
             }



            //if theres enough space for a 4 word pattern
            if(i < (englishText.size() - 3)){

                //rearrange according to a handwritten rule - pron1 adv pron2 v - pron1 adv v pron2
                //such as she swiftly him kicked = she swiftly kicked him 
                if(englishText.get(i).getPOS().contains("pron") && englishText.get(i+1).getPOS().contains("adv") 
                    && englishText.get(i+2).getPOS().contains("pron") && englishText.get(i+3).getPOS().contains("v") ){

                    Word w3 = englishText.get(i+2);
                    Word w4 = englishText.get(i+3);

                    englishText.set(i+2, w4);
                    englishText.set(i+3, w3);
                }

                //rearrange according to a handwritten rule - prep v det n = v det n prep
                //such as    from learn the password = learn the password from
                if(englishText.get(i).getPOS().contains("prep") && englishText.get(i+1).getPOS().contains("v") 
                    && englishText.get(i+2).getPOS().contains("det") && englishText.get(i+3).getPOS().contains("n") ){

                    Word w1 = englishText.get(i);
                    Word w2 = englishText.get(i+1);
                    Word w3 = englishText.get(i+2);
                    Word w4 = englishText.get(i+3);

                    englishText.set(i, w2);
                    englishText.set(i+1, w3);
                    englishText.set(i+2, w4);
                    englishText.set(i+3, w1);
                }

                //rearrange according to a handwritten rule - v det n prep = v prep det n  
                //such as  to teach the book from = to teach from the book
                if(englishText.get(i).getPOS().contains("v") && englishText.get(i+1).getPOS().contains("det") 
                    && englishText.get(i+2).getPOS().contains("n") && englishText.get(i+3).getPOS().contains("prep") ){

                    Word w2 = englishText.get(i+1);
                    Word w3 = englishText.get(i+2);
                    Word w4 = englishText.get(i+3);

                    englishText.set(i+1, w4);
                    englishText.set(i+2, w2);
                    englishText.set(i+3, w3);
                }

                //rearrange according to a handwritten rule - v det n adj = v det adj n
                //such as:   the elephant hit the tree same = the elephant hit the same tree
                if(englishText.get(i).getPOS().contains("v") && englishText.get(i+1).getPOS().contains("det") 
                    && englishText.get(i+2).getPOS().contains("n") && englishText.get(i+3).getPOS().contains("adj") ){

                    Word w3 = englishText.get(i+2);
                    Word w4 = englishText.get(i+3);

                    englishText.set(i+2, w4);
                    englishText.set(i+3, w3);
                }

                //rearrange according to the rule - pron/n v v v = pron/n v v
                //this rule basically just deletes the last verb at the beginning of a sentence or clause
                //if there are 3 verbs in a row, which almost never happens
                //i.e. I didn't have had a cheeseburger = I didn't have a cheeseburger
                if( (englishText.get(i).getPOS().contains("n") || englishText.get(i).getPOS().contains("pron") ) &&
                        englishText.get(i+1).getPOS().contains("v") && englishText.get(i+2).getPOS().contains("v") &&
                        englishText.get(i+3).getPOS().contains("v") ){

                    englishText.set(i+3, new Word(""));
                }


                if(i < (englishText.size() - 4) ){ //if theres enough space for a 5 word pattern

                    //rearrange according to a handwritten rule - det n det2 adv adj = det2 adv adj n
                    //such as =   the toy the most fun = the most fun toy
                    if(englishText.get(i).getPOS().contains("det") && englishText.get(i+1).getPOS().contains("n") 
                        && englishText.get(i+2).getPOS().contains("det") && englishText.get(i+3).getPOS().contains("adv") 
                        && englishText.get(i+4).getPOS().contains("adj") ){
                        
                        Word w2 = englishText.get(i+1);
                        Word w3 = englishText.get(i+2);
                        Word w4 = englishText.get(i+3);
                        Word w5 = englishText.get(i+4);

                        englishText.set(i, w3);
                        englishText.set(i+1, w4);
                        englishText.set(i+2, w5);
                        englishText.set(i+3, w2);
                        englishText.set(i+4, new Word(""));
                        }
                        


                }

            }
          }

        }

        //print out english text
        for (int i = 0; i<englishText.size(); i++){
            Word w = englishText.get(i);
            if(w.hasPOS()){
            	System.out.print(w.getWord()+" ");
            }
            else{
                System.out.print(w.getWord() + " ");
            }
        }
        
	}
	
	public static void main(String[] args){
		Translator MT = new Translator();
		
		// read the dictionary file, and store :
		// - the french words with POS
		// - the english words with POS
		// - the translation dictionary.
		MT.readDictionaryFile(new File("dictionary.txt"));
		
	    // read the french text, store the words and punctuation
		MT.readFrenchText(new File("amelie_nothomb.txt"));
		
		// translate the french text to english
		MT.translateToEnglish();

		System.out.println("\n");
		System.out.println("DONE");
	}

}


